package jp.co.systemi.asp.workflow.mail;

/**
 * リマインダーメール受信サーブレット
 */
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.co.systemi.asp.workflow.data.Reminder;
import jp.co.systemi.asp.workflow.data.ReminderMail;

import org.datanucleus.util.StringUtils;
import org.slim3.datastore.Datastore;

import com.google.appengine.api.datastore.Blob;

@SuppressWarnings("serial")
public class ReminderMailReceiverServlet extends HttpServlet {
    private static final Logger log = Logger
            .getLogger(ReminderMailReceiverServlet.class.getName());
    private static ResourceBundle mailBundle = ResourceBundle
            .getBundle(Mail.class.getName().replace(".", "/"));
    /** HTMLメールファイル名 */
    private int htmlFileNameCount = 0;

    private StringBuilder contents = new StringBuilder();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        ReminderMail reminderMail = new ReminderMail();
        Reminder reminder = new Reminder();
        String subject = "";
        log.info("メール解析");
        contents.setLength(0);
        try {
            Properties props = new Properties();
            Session session = Session.getDefaultInstance(props, null);
            MimeMessage message = new MimeMessage(session, req.getInputStream());
            log.info("subject: " + message.getSubject());
            subject = message.getSubject();
            // subject = new
            // String(message.getSubject().getBytes(mailBundle.getString("encoding")),
            // "UTF-8");
            String msgid = message.getMessageID();
            /* メール解析 */
            partAnalysis((Part) message);
            log.info("contents: " + contents.toString());
            // 予定をピックアップする
            // 日時： 2011/01/07　11:00～12:00
            String patternStr = "(\\d{4,4})[/-](\\d{1,2})[/-](\\d{1,2})[　 \\s]*(\\d{1,2})[:：](\\d{1,2})";
            Pattern pattern = Pattern.compile(patternStr);
            Matcher matcher = pattern.matcher(contents.toString());
            boolean blnMatch = matcher.find();
            log.info("regexp matched? " + blnMatch);
            if (blnMatch) {
                log.info("" + matcher.group(1) + "年" + matcher.group(2) + "月"
                        + matcher.group(3) + "日 " + matcher.group(4) + "時"
                        + matcher.group(5) + "分");
                reminderMail.setMsgBody(contents.toString());
                reminderMail.setMsgid(msgid);
                Calendar cal = Calendar.getInstance();
                cal.set(Integer.parseInt(matcher.group(1)), Integer
                        .parseInt(matcher.group(2)) - 1, Integer
                        .parseInt(matcher.group(3)), Integer.parseInt(matcher
                        .group(4)), Integer.parseInt(matcher.group(5)), 0);
                cal.set(Calendar.MILLISECOND, 0);
                // イベント日時
                reminderMail.setEventDate(cal.getTime());
                // デフォルト1日前
                cal.add(Calendar.DATE, -1);
                // リマインダーメール送信タイム
                reminder.setReminderDate(cal.getTime());
                reminderMail.setSubject(subject);
                List<String> froms = new ArrayList<String>();
                for (Address from : message.getFrom()) {
                    if (StringUtils.isEmpty(((InternetAddress) from)
                            .getPersonal())) {
                        log.info("from: "
                                + ((InternetAddress) from).getAddress());
                        froms.add(((InternetAddress) from).getAddress());
                    } else {
                        String fromName = new String(((InternetAddress) from)
                                .getPersonal().getBytes(
                                        mailBundle.getString("encoding")),
                                "UTF-8");
                        log.info("from: " + fromName);
                        froms.add(fromName + " <"
                                + ((InternetAddress) from).getAddress() + ">");
                    }
                }
                reminderMail.setFrom(froms);

                List<String> tos = new ArrayList<String>();
                for (Address to : message
                        .getRecipients(Message.RecipientType.TO)) {
                    if (!((InternetAddress) to).getAddress().matches(
                            "reminder.*")) {
                        if (StringUtils.isEmpty(((InternetAddress) to)
                                .getPersonal())) {
                            log.info("to: "
                                    + ((InternetAddress) to).getAddress());
                            tos.add(((InternetAddress) to).getAddress());
                        } else {
                            String toName = new String(((InternetAddress) to)
                                    .getPersonal().getBytes(
                                            mailBundle.getString("encoding")),
                                    "UTF-8");
                            log.info("to: " + toName);
                            tos
                                    .add(toName
                                            + " <"
                                            + ((InternetAddress) to)
                                                    .getAddress() + ">");
                        }
                    }
                }
                reminderMail.setTos(tos);
                // デフォルトはメール受信者全員へリマインダーを送信
                reminder.setOwner(tos);

                List<String> ccs = new ArrayList<String>();
                if (message.getRecipients(Message.RecipientType.CC) != null) {
                    for (Address cc : message
                            .getRecipients(Message.RecipientType.CC)) {
                        if (!((InternetAddress) cc).getAddress().matches(
                                "reminder.*")) {
                            if (StringUtils.isEmpty(((InternetAddress) cc)
                                    .getPersonal())) {
                                ccs.add(((InternetAddress) cc).getAddress());
                            } else {
                                ccs.add(((InternetAddress) cc).getPersonal()
                                        + " <"
                                        + ((InternetAddress) cc).getAddress()
                                        + ">");
                            }
                        }
                    }
                }
                reminderMail.setCcs(ccs);

                List<String> bccs = new ArrayList<String>();
                if (message.getRecipients(Message.RecipientType.BCC) != null) {
                    for (Address bcc : message
                            .getRecipients(Message.RecipientType.BCC)) {
                        if (!((InternetAddress) bcc).getAddress().matches(
                                "reminder.*")) {
                            if (StringUtils.isEmpty(((InternetAddress) bcc)
                                    .getPersonal())) {
                                bccs.add(((InternetAddress) bcc).getAddress());
                            } else {
                                bccs.add(((InternetAddress) bcc).getPersonal()
                                        + " <"
                                        + ((InternetAddress) bcc).getAddress()
                                        + ">");
                            }
                        }
                    }
                }
                reminderMail.setBccs(bccs);

                // データストアーに保存
                Datastore.put(reminderMail);
                reminder.setReminderMail(reminderMail.getId());
                Datastore.put(reminder);
            }
        } catch (MessagingException e) {
            log.info(e.getLocalizedMessage());
            // TODO エラーメール送信
        } catch (Exception e) {
            for (StackTraceElement ste : e.getStackTrace()) {
                log.info(ste.getClassName() + " " + ste.getMethodName() + " "
                        + ste.getLineNumber());
            }
            log
                    .info("error: " + e.getLocalizedMessage() + " "
                            + e.getMessage());
            // TODO エラーメール送信
        }
    }

    /**
     * メール解析
     *
     * @param part
     * @throws Exception
     */
    private void partAnalysis(Part part) throws Exception {

        if (part.isMimeType("multipart/*")) {
            /* マルチパートの場合 ------------------------------------------------- */

            Multipart content = (Multipart) part.getContent();

            // 含まれるパートを再帰的に処理
            int count = content.getCount();
            for (int i = 0; i < count; i++) {
                partAnalysis(content.getBodyPart(i));
            }

        } else if (part.isMimeType("text/plain")) {
            /* テキストの場合 ----------------------------------------------------- */

            InputStream is = (InputStream) part.getInputStream();
            Reader r = new InputStreamReader(is, mailBundle
                    .getString("encoding"));
            BufferedReader buf = new BufferedReader(r);

            /* テキスト内容 */
            for (String line; (line = buf.readLine()) != null;) {
                contents.append(line + "\n");
            }

        } else {
            // その他の場合

            /* 添付ファイルを保存 */
            getAttachments(part);
        }

    }

    /**
     * 添付ファイルを取得
     *
     * @param part
     * @throws MessagingException
     * @throws IOException
     */
    private void getAttachments(Part part) throws MessagingException,
            IOException {

        String disp = part.getDisposition();

        if (disp == null || disp.equalsIgnoreCase(Part.ATTACHMENT)) {
            /* 添付ファイルの場合 --------------------------------------------------- */
            log.info("filename " + part.getFileName());
            /* ファイル名 */
            if (part.getFileName() != null) {
                String filename = MimeUtility.decodeText(part.getFileName());
                if (part.isMimeType("text/html")) {
                    filename = String.valueOf(htmlFileNameCount++) + ".html";
                }

                /* ファイルサイズ */
                int fileSize = part.getSize();

                /* コンテントタイプ */
                String contentType = part.getContentType();

                /* ファイル内容 */
                Blob blob = this.generateBlob(part.getInputStream());

                log
                        .info("filename: " + filename + " fileSize: "
                                + fileSize + " contentType: " + contentType
                                + " contents: " + blob);
            }
        }
    }

    /**
     * Blob生成
     *
     * @param in
     * @return Blob
     * @throws IOException
     */
    private Blob generateBlob(InputStream in) throws IOException {

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int len;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer, 0, buffer.length)) != -1) {
            out.write(buffer, 0, len);
        }
        byte[] b = out.toByteArray();

        return new Blob(b);

    }

}
